package com.richard.base.net.api

import com.richard.base.net.BaseException
import com.richard.base.net.bean.KsResponse
import com.richard.base.net.bean.KsResult
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import java.io.IOException

/**
 * 基础网络请求封装 ，业务module 不要继承这个类。继承 ks_lightlearn_base #KsBaseRepository这个
 * @Author wuzhiguo
 *
 *
 * 需要跟服务端商量好
 * code =0 时就是正常的数据返回
 * code = -1是 异常，需要toast
 *
 *  0 -> response
1002, 4002 ->
return ReloginException(response.message)
4003, 1001 -> return TokenInvalidException()
4007 -> return DeviceIdInvalidException() // 暂时不会返回，先预留着。
1004 -> return OldTokenVerifyFailedException()
4001 ->
return TokenNotExistException(response.message)

1009 ->
除了以上这几个code处理 统一逻辑，不用其他code 处理业务逻辑 ，如果需要处理业务逻辑，让服务端加参数去区分。
 */

abstract class BaseRepository {

//    suspend fun <T : Any> apiCall(call: suspend () -> KsResponse<T>): KsResponse<T> {
//        return call.invoke()
//    }

    /**
     *  对网络请求过程中的异常进行了捕获，并返回自定义exception。
     *  @param call 实际进行网络请求 的方法 引用
     * @param errorMessage 调用方自定义 错误信息提示，比如错误了，就提示这个消息
     * @return KsResult ,
     */
    suspend fun <T : Any> safeApiCall(
        call: suspend () -> KsResult<T>,
        errorMessage: String? = null
    ): KsResult<T> {
        return try {
            val result = call()
            if(result is KsResult.Error){
                return handleException(result.exception,errorMessage,call)
            }
            return result
        } catch (e: Exception) {
            // An exception was thrown when calling the API so we're converting this to an IOException
            e.printStackTrace()
            KsResult.Error(
                exception = IOException(
                    if (errorMessage.isNullOrEmpty()) e.message else errorMessage,
                    e
                )
            )
        }
    }

    /**
     * 处理 异常，加入了 是否重试 选项
     * @param exception Exception 异常类
     * @param errorMessage String? 错误信息
     * @param call SuspendFunction0<KsResult<T>> ，重试需要调用的方法
     * @return KsResult<T> 返回结果
     */
    private suspend fun <T : Any> handleException(
        exception: Exception,
        errorMessage: String?,
        call: suspend () -> KsResult<T>
    ): KsResult<T> {
        if(exception is BaseException){
            println("handleException..........need retry:${exception.isNeedRetry},exception:$exception")
        }

        return if(exception is BaseException){
            if(exception.isNeedRetry){
               call()
            }else{
               KsResult.Error(code = exception.code,exception = exception)
            }

        }else  {
            println("不需要重试，返回错误 exception:${if (errorMessage.isNullOrEmpty()) exception.message else errorMessage}")
            KsResult.Error(
                exception = IOException(
                    if (errorMessage.isNullOrEmpty()) exception.message else errorMessage,
                    exception
                )
            )
        }
    }

    /**
     * @param
     */
    suspend fun <T : Any> executeResponse(
        response: KsResponse<T>, successBlock: (suspend CoroutineScope.() -> Unit)? = null,
        errorBlock: (suspend CoroutineScope.() -> Unit)? = null
    ): KsResult<T> {
        return coroutineScope {
            //
            when (val result = handleCode(response)) {
                is Exception -> {
                    errorBlock?.let { it() }
                    KsResult.Error(correctCode(response), result)
                }
                else -> {
                    successBlock?.let { it() }
                    KsResult.Success(
                        data = correctData(response),
                        code = correctCode(response),
                        msg = correctMessage(response)
                    )
                }
            }
        }
    }


    abstract suspend fun <T> handleCode(response: KsResponse<T>): Any


    protected fun <T> correctCode(response: KsResponse<T>): Int {
        return response.code ?: Int.MAX_VALUE
    }

    protected fun <T> correctMessage(response: KsResponse<T>): String {
        return response.msg ?: ""
    }

    private fun <T> correctData(response: KsResponse<T>): T? = with(response) {
        return data
    }


}